/**    
* BMap的所有library类均放在BMapLib命名空间下   
*/      
var BMapLib = window.BMapLib = BMapLib || {};      
(function () {      
      
    /**   
    * 地球半径   
    */      
    var EARTHRADIUS = 6370996.81;      
      
    /**   
    * GeoUtils类，静态类        
    */       
    var GeoUtils = BMapLib.GeoUtils = function () {}      
     
    /**   
    * 判断点是否在矩形内   
    * @param {Point} point 点对象   
    * @param {Bounds} bounds 矩形边界对象   
    * @returns {Boolean} 点在矩形内返回true,否则返回false   
    */      
    GeoUtils.isPointInRect = function (point, bounds) {      
        //检查类型是否正确      
        if (!(point instanceof BMap.Point) ||      
             !(bounds instanceof BMap.Bounds)) {      
            return false;      
        }      
        var sw = bounds.getSouthWest(); //西南脚点      
        var ne = bounds.getNorthEast(); //东北脚点      
        return (point.lng >= sw.lng && point.lng <= ne.lng && point.lat >= sw.lat && point.lat <= ne.lat);      
    } 
      
    /**   
    * 判断点是否在圆形内   
    * @param {Point} point 点对象   
    * @param {Circle} circle 圆形对象   
    * @returns {Boolean} 点在圆形内返回true,否则返回false   
    */      
    GeoUtils.isPointInCircle = function (point, circle) {      
        //检查类型是否正确      
        if (!(point instanceof BMap.Point) ||      
            !(circle instanceof BMap.Circle)) {      
            return false;      
        }      
      
        //point与圆心距离小于圆形半径，则点在圆内，否则在圆外      
        var c = circle.getCenter();      
        var r = circle.getRadius();      
        var dis = GeoUtils.getDistance(point, c);      
        if (dis <= r) {      
            return true;      
        } else {      
            return false;      
        }      
    }      
      
      
    /**   
    * 判断点是否多边形内   
    * @param {Point} point 点对象   
    * @param {Polyline} polygon 多边形对象   
    * @returns {Boolean} 点在多边形内返回true,否则返回false   
    */      
    GeoUtils.isPointInPolygon = function (point, polygon) {      
        //检查类型      
        if (!(point instanceof BMap.Point) ||      
             !(polygon instanceof BMap.Polygon)) {      
            return false;      
        }      
      
        //首先判断点是否在多边形的外包矩形内，如果在，则进一步判断，否则返回false      
        var polygonBounds = polygon.getBounds();      
        if (!this.isPointInRect(point, polygonBounds)) {      
            return false;      
        }      
      
        var pts = polygon.getPath(); //获取多边形点      
      
        //基本思想是利用射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则      
        //在多边形内。还会考虑一些特殊情况，如点在多边形顶点上，点在多边形边上等特殊情况。      
      
        var N = pts.length;      
        var boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true      
        var intersectCount = 0;     
        var precision = 2e-10; //浮点类型计算时候与0比较时候的容差      
        var p1, p2;     
        var p = point; //测试点      
      
        p1 = pts[0];             
        for (var i = 1; i <= N; ++i) {                  
            if (p.equals(p1)) {      
                return boundOrVertex;      
            }      
      
            p2 = pts[i % N];                  
            if (p.lat < Math.min(p1.lat, p2.lat) || p.lat > Math.max(p1.lat, p2.lat)) {                  
                p1 = p2;      
                continue;     
            }      
            if (p.lat > Math.min(p1.lat, p2.lat) && p.lat < Math.max(p1.lat, p2.lat)) {
                if (p.lng <= Math.max(p1.lng, p2.lng)) {                         
                    if (p1.lat == p2.lat && p.lng >= Math.min(p1.lng, p2.lng)) {     
                        return boundOrVertex;      
                    }      
      
                    if (p1.lng == p2.lng) {              
      
      
                        if (p1.lng == p.lng) {      
                            return boundOrVertex;      
                        } else {    
                            ++intersectCount;      
                        }      
                    } else {                
      
      
                        var xinters = (p.lat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;                    
      
      
                        if (Math.abs(p.lng - xinters) < precision) {    
                            return boundOrVertex;      
                        }      
      
                        if (p.lng < xinters) {  
                            ++intersectCount;      
                        }      
                    }      
                }      
            } else {                    
                if (p.lat == p2.lat && p.lng <= p2.lng) {                         
                    var p3 = pts[(i + 1) % N];              
      
      
                    if (p.lat >= Math.min(p1.lat, p3.lat) && p.lat <= Math.max(p1.lat, p3.lat)) {    
                        ++intersectCount;      
                    } else {      
                        intersectCount += 2;      
                    }      
                }      
            }      
            p1 = p2;   
        }      
      
        if (intersectCount % 2 == 0) {//偶数在多边形外      
            return false;      
        } else { //奇数在多边形内      
            return true;      
        }      
    }      
      
    /**   
    * 将度转化为弧度   
    * @param {degree} Number 度        
    * @returns {Number} 弧度   
    */      
    GeoUtils.degreeToRad = function (degree) {      
        return Math.PI * degree / 180;      
    }      
      
    /**   
    * 将v值限定在a,b之间，纬度使用   
    */      
    function _getRange(v, a, b) {      
        if (a != null) {      
            v = Math.max(v, a);      
        }      
        if (b != null) {      
            v = Math.min(v, b);      
        }      
        return v;      
    }      
      
    /**   
    * 将v值限定在a,b之间，经度使用   
    */      
    function _getLoop(v, a, b) {      
        while (v > b) {      
            v -= b - a      
        }      
        while (v < a) {      
            v += b - a      
        }      
        return v;      
    }      
      
    /**   
    * 计算两点之间的距离,两点坐标必须为经纬度   
    * @param {point1} Point 点对象   
    * @param {point2} Point 点对象   
    * @returns {Number} 两点之间距离，单位为米   
    */      
    GeoUtils.getDistance = function (point1, point2) {      
        //判断类型      
        if (!(point1 instanceof BMap.Point) ||      
             !(point2 instanceof BMap.Point)) {      
            return 0;      
        }      
      
        point1.lng = _getLoop(point1.lng, -180, 180);      
        point1.lat = _getRange(point1.lat, -74, 74);      
        point2.lng = _getLoop(point2.lng, -180, 180);      
        point2.lat = _getRange(point2.lat, -74, 74);      
      
        var x1, x2, y1, y2;      
        x1 = GeoUtils.degreeToRad(point1.lng);      
        y1 = GeoUtils.degreeToRad(point1.lat);      
        x2 = GeoUtils.degreeToRad(point2.lng);      
        y2 = GeoUtils.degreeToRad(point2.lat);      
      
        return EARTHRADIUS * Math.acos((Math.sin(y1) * Math.sin(y2) + Math.cos(y1) * Math.cos(y2) * Math.cos(x2 - x1)));      
    }      
      
 })(BMapLib)
